#include <Poco/Data/Session.h>

#include "../../include/CLink/database/Transaction.h"
#include "../../include/CLink/database/DatabaseError.h"
#include "../../include/CLink/database/ConnectionPool.h"
namespace CLink
{
namespace Db
{

Transaction::Transaction(ConnectionPool::ConnectionPtr connection, IsolationLevel level)
    : _connection(connection), _state(TransactionState::ACTIVE), _level(level)
{
    if (!_connection)
    {
        throw TransactionException("Invalid database connection");
    }
    begin();
}

Transaction::~Transaction()
{
    if (_state == TransactionState::ACTIVE)
    {
        try
        {
            rollback();
        }
        catch (...)
        {
        }
    }
}

void Transaction::commit()
{
    if (_state != TransactionState::ACTIVE)
    {
        throw TransactionException("Transaction has already ended");
    }
    _connection->commit();
    _state = TransactionState::COMMITTED;
}

void Transaction::rollback()
{
    if (_state != TransactionState::ACTIVE)
    {
        throw TransactionException("Transaction has already ended");
    }
    _connection->rollback();
    _state = TransactionState::ROLLED_BACK;
}

void Transaction::begin()
{
    try
    {
        setIsolationLevel();
        _connection->begin();
    }
    catch (const std::exception &e)
    {
        throw TransactionException(std::string("Failed to start transaction: ") + e.what());
    }
}

TransactionState Transaction::getState() const
{
    return _state;
}

void Transaction::setIsolationLevel()
{
    std::string sql;
    switch (_level)
    {
    case IsolationLevel::READ_UNCOMMITTED:
        sql = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
        break;
    case IsolationLevel::READ_COMMITTED:
        sql = "SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
        break;
    case IsolationLevel::REPEATABLE_READ:
        sql = "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ";
        break;
    case IsolationLevel::SERIALIZABLE:
        sql = "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE";
        break;
    }
    _connection->getSession() << sql;
}

TransactionGuard::TransactionGuard(Transaction &transaction)
    : _transaction(transaction) {}

TransactionGuard::~TransactionGuard()
{
    if (_transaction.getState() == TransactionState::ACTIVE)
    {
        try
        {
            _transaction.commit();
        }
        catch (...)
        {
            _transaction.rollback();
        }
    }
}

std::atomic<uint64_t> NestedTransaction::_savepointCounter(0);

NestedTransaction::NestedTransaction(ConnectionPool::ConnectionPtr connection)
    : _connection(connection)
{
    if (!_connection)
    {
        throw TransactionException("Invalid database connection");
    }
    _savepointName = "SAVEPOINT_" + std::to_string(_savepointCounter++);
    _connection->getSession() << "SAVEPOINT " + _savepointName;
}

NestedTransaction::~NestedTransaction()
{
    if (_active)
    {
        try
        {
            rollback();
        }
        catch (...)
        {
        }
    }
}

void NestedTransaction::commit()
{
    if (!_active)
    {
        throw TransactionException("Nested transaction has already ended");
    }
    _connection->getSession() << "RELEASE SAVEPOINT " + _savepointName;
    _active = false;
}

void NestedTransaction::rollback()
{
    if (!_active)
    {
        throw TransactionException("Nested transaction has already ended");
    }
    _connection->getSession() << "ROLLBACK TO SAVEPOINT " + _savepointName;
    _active = false;
}

} // namespace db
} // namespace CLink